# Juan Pablo Barba Muñoz

import MySQLdb
import yaml
import pprint
import os
import shutil
import curses
import logging
import datetime
import dbrandom
import random
import string

from warnings import filterwarnings
from jinja2 import Environment, PackageLoader
from optparse import OptionParser

def random_data(type, prefix='', size=10):
	if(type=='string'):
		return dq(_random_string(prefix,size))
	if(type=='integer'):
		return str(_ranfom_int())
	if(type=='map'):
		return dq('8,8')
	if type=='calendar':
		now=datetime.datetime.now()		
		return dq(str(now.strftime("%Y-%m-%d %H:%M")))
	if type=='editor':
		return dq(_random_string(size=100))
	if type=='sql':
		return str(random.randint(1,20))

def _random_string(prefix='', size=10):
	alphabet = 'abcdefghijklmnopqrstuvwxyz'
	
	string=''
	for i in range(1,size):
	  	string=string+alphabet[random.randint(0,len(alphabet)-1)]
  	
  	return prefix+string

def _random_int():
	return random.randint(1, 10000)

def quotes(string):	
	return '\''+string+'\''

def dq(string):
	return '\"'+string+'\"'

def _ensure_dir(path):
		
	try:
		os.makedirs(path)
	except:
		logging.debug('El dirctorio '+path+' ya xiste')
	return path

def _create_if_not_exists(path):
	if not os.path.exists(path):
		f=open(path, 'w')
		f.close()

class JGen:
	def __init__(self, options):
		self.config=self._parse_config()
		self.env=Environment(loader=PackageLoader('jgen', 'templates'+os.sep+'25'))
		self.options=options
		
	def _get_template(self, template):
		return self.env.get_template(template)
		
	def _db_connect(self):
		db=MySQLdb.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'], self.config['db_name'])
		return db

	def _parse_config(self):
		logging.info('Reading config')	
		f=open('config.yaml')
		config=yaml.load(f)
		f.close();

		return config
	
	def _db_install_component(self):		
		return
		
	def _db_test(self):
		db=self._db_connect()
		db.close()
		
	def _apply_template(self, template, path, filename, entity=None, params=None):
		try:
			f=open(path+os.sep+filename ,'w')
			self.env.globals['filename']=filename
			template = self._get_template(template)
			f.write(template.render(config=self.config, entity=entity, params=params))
			f.close()
		except Exception as e:
			print 'Error aplicando la plantilla '+str(template)
			print e
	
	def _generate_root_files(self):
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin')
		logging.info('	Generando controlador principal')
		self._apply_template('templateController.php', path, 'controller.php')
		_create_if_not_exists(path+os.sep+'index.html')
		
		logging.info('	Generando frontend entrada componente')
		self._apply_template('templateFrontend.php', path, self.config['component']['name']+'.php')

	def _generate_helpers(self):
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'helpers')
		# Generamos los helpers	
		self._apply_template('templateHelper.php', path, self.config['component']['name'].lower()+'.php')
		_create_if_not_exists(path+os.sep+'index.html')		

	def _generate_entities(self):
		
		logging.info('Generando entidades')
		for entity in self.config['component']['entities']:
			self._generate_entity(entity)

	def _generate_models(self, entity):
		# Generamos los modelos para los formularios
		logging.info('	Generando modelo formulario para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'models')
		self._apply_template('modelDetail.php', path, entity['name']+'.php', entity)
		_create_if_not_exists(path+os.sep+'index.html')		
		
		# Generamos el xml del form
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'models'+os.sep+'forms')
		self._apply_template('templateForm.xml', path, entity['name']+'.xml', entity)			
		_create_if_not_exists(path+os.sep+'index.html')		

		# Generamos los modelos para los listados
		logging.info('\tGenerando modelo listado para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'models')
		self._apply_template('modelList.php', path, entity['name']+'list.php', entity)						
		_create_if_not_exists(path+os.sep+'index.html')		


	def _generate_tables(self, entity):
		# Generamos las tablas
		logging.info('\tGenerando tabla para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'tables')
		self._apply_template('templateTable.php', path, entity['name']+'.php', entity)									
		_create_if_not_exists(path+os.sep+'index.html')	
		
	def _generate_views(self, entity):
	
		# Generamos las vistas
		logging.info('\tGenerando fichero de vista de formulario para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'views'+os.sep+entity['name'])
		self._apply_template('templateViewForm.html.php', path, 'view.html.php', entity)												
		_create_if_not_exists(path+os.sep+'index.html')		
		
		logging.info('\tGenerando fichero de vista de listado para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'views'+os.sep+entity['name']+'list')
		self._apply_template('templateViewList.html.php', path, 'view.html.php', entity)			
		_create_if_not_exists(path+os.sep+'index.html')	
		
		# Generamos las plantillas de las vistas
		logging.info('\tGenerando fichero de template de vista de formulario para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'views'+os.sep+entity['name']+os.sep+'tmpl')
		self._apply_template('templateEdit.php', path, 'edit.php', entity)						
		_create_if_not_exists(path+os.sep+'index.html')							

		logging.info('\tGenerando fichero de template de modals para: '+entity['name'])
		self._apply_template('templateMapModal.php', path, 'map.php', entity)						
		_create_if_not_exists(path+os.sep+'index.html')

		# Generamos las plantillas de la vista de listado
		logging.info('\tGenerando fichero de template de vista de listado para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'views'+os.sep+entity['name']+'list'+os.sep+'tmpl')
		self._apply_template('templateList.php', path, 'default.php', entity)									
		_create_if_not_exists(path+os.sep+'index.html')				

		visibleFields=0					
		for field in entity['fields']:
			try:
				if  field['list_options'].count('visible'):
					visibleFields=visibleFields+1
			except:
				pass

		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'views'+os.sep+entity['name']+'list'+os.sep+'tmpl')
		self._apply_template('default_body.php', path, 'default_body.php', entity, {'visibleFields': visibleFields})									
		self._apply_template('default_foot.php', path, 'default_foot.php', entity, {'visibleFields': visibleFields})												
		self._apply_template('default_head.php', path, 'default_head.php', entity, {'visibleFields': visibleFields})																			

		_create_if_not_exists(self.config['dstpath']+os.sep+'admin'+os.sep+'views'+os.sep+'index.html')

	def _generate_controllers(self, entity):
		# Generamos el controlador para el formulario
		logging.info('\tGenerando fichero controller para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'controllers')
		self._apply_template('controllerForm.php', path, entity['name']+'.php', entity)
		_create_if_not_exists(path+os.sep+'index.html')	

		# Generamos el controlador para el listado			
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'controllers')
		self._apply_template('controllerList.php', path, entity['name']+'list.php', entity)
		_create_if_not_exists(path+os.sep+'index.html')	

	def _generate_fields(self, entity):
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'models'+os.sep+'fields')
		self._apply_template('/fields/map.php', path, 'map.php', entity)
		_create_if_not_exists(path+os.sep+'index.html')		
				
	def _generate_entity(self, entity):
		logging.info('Generando entidad '+entity['name'])
		
		# Backend
		self._generate_models(entity)
		print entity['fields']
		self._generate_tables(entity)		
		self._generate_views(entity)
		self._generate_controllers(entity)	
		self._generate_fields(entity)

		self._db_update_entity(entity)	
		
		if self.options.populate:
			self._db_populate_entity(entity)		
			
		# Frontend
		self._generate_frontend_controllers(entity)
		self._generate_frontend_views(entity)
		
	def _generate_frontend_views(self, entity):
	
		# Generamos las vistas
		logging.info('\tGenerando fichero de vista de formulario para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'site'+os.sep+'views'+os.sep+entity['name'])
		self._apply_template('frontend/view.html.php.tpl', path, 'view.html.php', entity)												
		_create_if_not_exists(path+os.sep+'index.html')		
		
		logging.info('\tGenerando fichero de vista de listado para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'site'+os.sep+'views'+os.sep+entity['name']+'list')
		self._apply_template('frontend/view.html.php.tpl', path, 'view.html.php', entity)			
		_create_if_not_exists(path+os.sep+'index.html')	
		
		# Generamos las plantillas de las vistas
		logging.info('\tGenerando fichero de template de vista de formulario para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'site'+os.sep+'views'+os.sep+entity['name']+os.sep+'tmpl')
		self._apply_template('frontend/default.php.tpl', path, 'default.php', entity)						
		self._apply_template('frontend/default.xml.tpl', path, 'default.xml', entity)
		_create_if_not_exists(path+os.sep+'index.html')							

		# Generamos las plantillas de la vista de listado
		logging.info('\tGenerando fichero de template de vista de listado para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'site'+os.sep+'views'+os.sep+entity['name']+'list'+os.sep+'tmpl')
		self._apply_template('frontend/default.php.tpl', path, 'default.php', entity)									
		self._apply_template('frontend/defaultList.xml.tpl', path, 'default.xml', entity)
		_create_if_not_exists(path+os.sep+'index.html')				
	
	
	def _generate_frontend_controllers(self, entity):		
		# Generamos el controlador para el detalle de un item
		logging.info('\tGenerando fichero controller para: '+entity['name'])
		path=_ensure_dir(self.config['dstpath']+os.sep+'site'+os.sep+'controllers')
		self._apply_template('frontend/controller.php.tpl', path, entity['name']+'.php', entity)
		_create_if_not_exists(path+os.sep+'index.html')	

		# Generamos el controlador para el listado			
		path=_ensure_dir(self.config['dstpath']+os.sep+'site'+os.sep+'controllers')
		self._apply_template('frontend/controller.php.tpl', path, entity['name']+'list.php', entity)
		_create_if_not_exists(path+os.sep+'index.html')	

		
	def _db_update_entity(self, entity):
		logging.info('\tGenerando entidad en base de datos')
		db=self._db_connect()
		cursor=db.cursor()	
		table_name=self.config['db_prefix']+self.config['component']['name']+'_'+entity['name']
		sql='DROP TABLE IF EXISTS '+table_name
		logging.debug(sql)
		cursor.execute(sql)
		
		sql='CREATE TABLE '+table_name+'('
		#sql=sql+'id int auto_increment primary key, created_by int, state tinyint(1)'	
		sql=sql+'id int auto_increment primary key'	
		

		for field in entity['fields']:
			sql=sql+', '+field['name']+' '+self._translate_types_2_db(field)

			
		sql=sql+');'
				
		logging.debug(sql)

		cursor.execute(sql)
	
		db.commit()				
		db.close()
		
	def _db_populate_entity(self, entity):
		logging.info('\tGenerando datos para entidad en base de datos')
		db=self._db_connect()
		cursor=db.cursor()	
		
		table_name=self.config['db_prefix']+self.config['component']['name']+'_'+entity['name']
		
		for i in range(1,20):
			sqlInsert=''
			valuesInsert=''
			for field in entity['fields']:
				sqlInsert=sqlInsert+field['name']+','
				valuesInsert=valuesInsert+random_data(field['type'], field['name'])+',';		

			sqlInsert=sqlInsert[:-1]			
			valuesInsert=valuesInsert[:-1]


			sql='INSERT INTO '+table_name+'('+sqlInsert+') VALUES ('+valuesInsert+')'
			logging.debug(sql)
			cursor.execute(sql)
		
		db.commit()
		db.close()
		
	def _db_create_component(self):
	
		logging.info('Creando componente en base de datos')
		db=self._db_connect()
		cursor=db.cursor()
		# Insertamos en la tabla la extension
		sql='INSERT INTO {1}extensions (name, type, element, client_id, enabled, access, protected) VALUES (\'com_{0}\', \'component\', \'com_{0}\', 1, 1, 0, 0)'
		sql=sql.format(self.config['component']['name'].lower(), self.config['db_prefix'])
		logging.info('\tInsertando en la tabla '+self.config['db_prefix']+'extensions')
		logging.debug(sql)
		cursor.execute(sql)		
		idComponent=cursor.lastrowid		
		
		# Creamos entradas para el menu principal	
		sql='INSERT INTO '+self.config['db_prefix']+'menu (menutype, title, alias, note, path, link, type, published, parent_id, level, component_id, browserNav, access, home, client_id, img) VALUES( \'main\', '+quotes('COM_'+self.config['component']['name']+"_MENU").upper()+' , '+quotes('com-'+self.config['component']['name'])+', \'\', '+quotes('com-'+self.config['component']['name'])+', '+quotes('index.php?option=com_'+self.config['component']['name'].lower())+', \'component\', 1, 1, 1, '+str(idComponent)+', 0, 1, 0, 1,'+quotes('components/com_'+self.config['component']['name'].lower()+'/images/main-icon.png')+')'
		logging.info('\tInsertando en la tabla '+self.config['db_prefix']+'menu')
		logging.debug(sql)
		
		cursor.execute(sql)
		
		db.commit()
		db.close()
	
	def _generate_language_files(self, language='es-ES'):
		path=_ensure_dir(self.config['dstpath']+os.sep+'admin'+os.sep+'languages'+os.sep+language)

		logging.info('Generando fichero de idioma .sys.ini')
		f=open(path+os.sep+language+'.com_'+self.config['component']['name']+'.sys.ini','w')
		f.write('COM_'+self.config['component']['name'].upper()+'_MENU='+dq(self.config['component']['title'])+'\n')		
		f.write('COM_'+self.config['component']['name'].upper()+'='+dq(self.config['component']['title'])+'\n')
		f.close()

		logging.info('Generando fichero de idioma .ini')		
		f=open(path+os.sep+language+'.com_'+self.config['component']['name']+'.ini','w')		
		f.write('COM_'+self.config['component']['name'].upper()+'='+dq(self.config['component']['title'])+'\n')
		
		for entity in self.config['component']['entities']:
			f.write('COM_'+self.config['component']['name'].upper()+'_'+entity['name'].upper()+'='+dq(entity['name'].capitalize())+'\n')
			# Mensaje de gestion
			f.write('COM_'+self.config['component']['name'].upper()+'_LIST_'+entity['name'].upper()+'='+dq('Listado de '+entity['name'])+'\n')
			# Mensaje de edicion
			f.write('COM_'+self.config['component']['name'].upper()+'_'+entity['name'].upper()+'_EDIT='+dq('Edicion de '+entity['name'])+'\n')
			f.write('COM_'+self.config['component']['name'].upper()+'_'+entity['name'].upper()+'_NEW='+dq('Alta de '+entity['name'])+'\n')			
			for field in entity['fields']:
				f.write('COM_'+self.config['component']['name'].upper()+'_'+entity['name'].upper()+'_'+field['name'].upper()+'='+dq(field['name'].capitalize())+'\n')

		f.write('COM_'+self.config['component']['name'].upper()+'_1_DELETED='+dq('Se ha borrado el elemento correctamente')+'\n')
		f.write('COM_'+self.config['component']['name'].upper()+'_N_DELETED='+dq('Se han borrado los %d elementos corrctamente')+'\n')
		
		f.close()
		
	def _translate_types_2_db(self, field):
		
		logging.debug(field)
		type=field['type']
		
		if type=='integer':
			return 'int'
		if type=='string':
			size=50 if not field['size'] else field['size'];
			return 'varchar('+str(size)+')'
		if type=='calendar':
			return 'date'
		if type=='map':
			return 'varchar(50)'			
		if type=='editor':
			return 'text'
		if type=='sql':
			return 'integer'

	def _copy_media_files(self):
		logging.info('Copiando media ...')
		shutil.copytree('images', self.config['dstpath']+os.sep+'media')
		
	def generate(self):	
		now=datetime.datetime.now()		
		self.env.globals['now']=str(now.strftime("%Y-%m-%d %H:%M"))
		self._generate_root_files()
		self._generate_helpers()
		self._generate_entities()
		self._db_create_component()
		self._generate_language_files()
		self._copy_media_files()

	def update_entity(self, entityName):
		logging.info('Updating entity '+entityName)
		try:
			entity = filter(lambda e: e['name'] == entityName, self.config['component']['entities'])[0]
			self._generate_entity(entity)	
		except:
			print 'Unknown entity'

	def clean(self):
		logging.info('Eliminando proyecto ...')
		try:
			shutil.rmtree(self.config['dstpath'])
		except:
			pass
			
		# Borramos de la base de datos las tablas y entradas
		db=self._db_connect()
		cursor=db.cursor()		
		
		# Borramos la entrada de la extension
		sql='DELETE FROM '+self.config['db_prefix']+'extensions WHERE name='+quotes('com_'+self.config['component']['name'].lower())
		cursor.execute(sql)		
		
		# Borramos la tabla de cada entidad
		for entity in self.config['component']['entities']:
			sql='DROP TABLE IF EXISTS '+self.config['db_prefix']+self.config['component']['name']+'_'+entity['name']
			cursor.execute(sql)
		
		# Borramos las entradas del menu
		sql='DELETE FROM '+self.config['db_prefix']+'menu WHERE alias='+quotes('com-'+self.config['component']['name'].lower())
		cursor.execute(sql)
		
		db.commit()
		db.close()	

def config_options():
	parser = OptionParser()
	parser.add_option('-l', '--loglevel', dest='loglevel', default='INFO', help='Level og logging')
	parser.add_option('-c', '--clean', action='store_true', dest='clean', default=False, help='Force clean up')	
	parser.add_option('-e', '--update-entity', dest='entityName', help='Entity name to update')
	parser.add_option('-p', '--populate', action='store_true', dest='populate', help='Populate database')
	
	return parser.parse_args()			

if __name__ == '__main__':

	try:
		(options, args)=config_options()

	except:
		exit()

	logging.basicConfig(level=eval('logging.'+options.loglevel.upper()))	
	filterwarnings('ignore', category = MySQLdb.Warning) # Disable MySQL warnings
	
	j=JGen(options)
	
	if options.entityName:
		j.update_entity(options.entityName)
	else:	
		j.clean()
		j.generate()
	


	


